﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////

// Reviewed
#pragma once
#include "GcmpCommandAction.h"
#include "IAction.h"
#include "ISnapCandidatesPreprocessor.h"

namespace gcmp
{
    class IDocument;
    class IGraphicsNodeReference;
    class IGraphicsBRepBody;
    class IGraphicsElementShape;
    class IModelView;
    class IGraphicsNodeGroup;
    class IControlDefinition;
    class IActionRequest;
    enum class CursorType : int32_t;


    /// \brief 操作（Action）实现基类，用于派生实现自定义Action。
    class GCMP_COMMAND_ACTION_EXPORT ActionBase : public gcmp::IAction
    {
    public:
        ActionBase();
        virtual ~ActionBase();
    public:
        /// \brief Action被启动后，接收任何用户输入之前，该方法会被调用。
        /// \see IAction
        virtual void InitAction(IUiView* pCurrentView) override;

        /// \brief Action被强制退出时（比如用户按了Esc键，或者点击了其它同级别的命令），该方法会被调用。
        /// \see IAction
        virtual void ActionCancelled() override;

        /// \brief 获取Action执行的返回参数,含执行状态和数据
        /// \see IAction
        virtual void GetActionOutput(ActionOutput& param) const override;

        /// \brief 鼠标左键按下事件处理
        /// \see IAction
        virtual bool OnLButtonDown(IUiView* pCurrentView, const Vector3d& pos) override;
        
        /// \brief 鼠标左键弹起事件处理
        /// \see IAction
        virtual bool OnLButtonUp(IUiView* pCurrentView, const Vector3d& pos) override;
        
        /// \brief 鼠标左键双击事件处理
        /// \see IAction
        virtual bool OnLButtonDoubleClick(IUiView* pCurrentView, const Vector3d& pos) override;
        
        /// \brief 鼠标右键按下事件处理
        /// \see IAction
        virtual bool OnRButtonDown(IUiView* pCurrentView, const Vector3d& pos) override;
        
        /// \brief 鼠标右键弹起事件处理
        /// \see IAction
        virtual bool OnRButtonUp(IUiView* pCurrentView, const Vector3d& pos) override;
        
        /// \brief 鼠标移动事件处理
        /// \see IAction
        virtual bool OnMovePoint(IUiView* pCurrentView, const Vector3d& pos) override;
        
        /// \brief 键盘按下事件处理
        /// \see IAction
        virtual bool OnKeyDown(IUiView* pCurrentView, int nChar) override;

        /// \brief 按健弹起事件处理
        /// \see IAction
        virtual bool OnKeyUp(IUiView* pCurrentView, int nChar) override;

        /// \brief 视图内输入事件处理
        /// \see IAction
        virtual bool OnReceiveViewInputData(IUiView* pCurrentView, const ActionViewInput& inputParam) override;

        /// \brief 当用户点击一个界面按钮或菜单项执行命令时，该方法会被调用。
        /// \see IAction
        virtual bool OnCommand(IUiView* pCurrentView, const std::wstring& idString, const CommandParameters& cmdParams) override;
        
        /// \brief 当视图被切换后，该方法会被调用（使用前提Action支持跨视图，跨视图Action需要同时重载PreviewViewSwitch()）。
        /// \see IAction
        virtual bool OnViewSwitched(IUiView* pUIView) override;

        /// \brief 当该Action之上的子Action结束后，该方法会被调用。
        /// \see IAction
        virtual void OnChildActionFinished(IUiView* pCurrentView, const ActionOutput& childActionReturnParam) override;

        /// \brief 获取Action光标
        virtual std::wstring GetCursorPath() const override;

        /// \brief 获取Action当前鼠标状态提示
        virtual std::wstring GetMouseTooltipMessage() const override;

        /// \brief 设置Action鼠标右键上下文菜单
        /// \see IAction
        virtual OwnerPtr<IMenuItemContainerDefinition> PrepareContextMenu(IUiView* pUIView) override;

        /// \brief 获取Action当前的提示字符串
        virtual std::wstring GetPromptMessage() const override;

        /// \brief 获取当前Action接受输入长度参数有效范围。
        /// \see IAction
        virtual void GetValidRangeOfLength(double& minLen /*= gcmp::Constants::MIN_MODEL_SIZE*/, double& maxLen /*= gcmp::Constants::MAX_MODEL_SIZE*/) const override;
        
        /// \brief 获取当前Action接受输入角度参数有效范围。
        /// \see IAction
        virtual void GetValidRangeOfAngle(double& minAng /*= -180*/, double& maxAng /*= 180*/) const override;
    
        /// \brief 如果子类存在跨视图行为，需要重载这个函数，同时重载OnViewSwitched(IUiView* pUIView)
        /// \see IAction
        virtual bool PreviewViewSwitched() override;

        /// \brief  重载此方法以告知系统是否在顶层Action处理键盘事件前先预览并处理键盘相关事件。
        /// \see IAction
        virtual bool PreviewKeyEvent() override;

        /// \brief  重载此方法以告知系统是否在顶层Action处理键盘事件前先预览并处理键盘相关事件。
        /// \see IAction
        virtual bool PreviewMouseEvent() override;

        /// \brief  重载此方法以告知系统是否在顶层Action处理键盘事件前先预览并处理键盘相关事件。
        /// \see IAction
        virtual bool PreviewCommand() override;

        /// \brief 设置启动Action的Command路径
        /// \param path Action的启动路径，最后一个是启动当前Action的Command
        /// \return 
        void SetCommandChain(const std::list<std::wstring>& cmdChain);

        /// \brief 返回当前Action的启动路径
        /// \return 当前Action的启动路径，最后一个是启动当前Action的Command
        virtual const std::list<std::wstring>& CommandChain() const override;
        //////////////////////////////////////////////////////////////////////////
        /// \brief 暂时保留,用于保存文档时记录启动Action的命令，完成后恢复到当前Action
        /// \see IAction
        virtual void SetCommandId(const std::wstring& commandId) override;
        /// \brief 暂时保留,用于保存文档时记录启动Action的命令，完成后恢复到当前Action
        /// \see IAction
        virtual std::wstring GetCommandId() const override;

        /// \brief 暂时保留 回调, 根据当前的视图输入状态, 刷新视图内的临时对象（视图内输入是重定位点需要）
        /// \see IAction
        virtual bool OnMoveCallback(IUiView* pCurrentView) override;

        //////////////////////////////////////////////////////////////////////////
        // TODO：这个方法去掉工作量巨大，暂时搁置保留，注意：请勿使用！！请勿使用！！！
        // Ui 替换
        virtual void SetUiDocument(gcmp::IUiDocument* pUIDoc) override;
        //////////////////////////////////////////////////////////////////////////

        // 供外部调用的功能
    public:  
        /// \brief 设置状态栏上的提示字符串
        void SetPromptMessage(const std::wstring& message);

        // iAction提供Get接口
        /// \brief 设置鼠标提示信息
        void SetMouseTooltipMessage(const std::wstring& message);

        /// \brief 获取光标类型
        CursorType GetCursorType() const;

        /// \brief 设置光标类型
        void SetCursorType(CursorType cursorType);

        //////////////////////////////////////////////////////////////////////////
        // 设置动态按钮数据，将被删除。
        //void AddDynamicGroup(const std::wstring& pageName, std::vector<gcmp::OwnerPtr<gcmp::CtrlGroup> >&& dynamicGroups, const std::wstring& contextualPageCaption = L"");
        //void AddDynamicGroup(const std::wstring& pageName, std::vector<gcmp::OwnerPtr<gcmp::IRibbonGroup> >&& dynamicGroups, const std::wstring& contextualPageCaption = L"");
 
        // 注释规范
        // 寄送请求：请求将在InitAction()或最近的OnChildActionFinished()执行完毕后由框架负责响应；
        // 目前，交互请求的类别仅有：切换视图。以后可按需添加
        void PostRequest(gcmp::OwnerPtr<IActionRequest> opRequest);
        //////////////////////////////////////////////////////////////////////////

        // 预设参数
    public:
        /// \brief 设置预设参数对象Id
        void SetPreSetElementId(const gcmp::ElementId& elementId);

        /// \brief 获取预设参数对象Id
        gcmp::ElementId GetPreSetElementId() const;

        /// \brief  清理临时的预设参数构件
        void DeletePreSetElement();

        // 周阳确认下行为
        /// \brief 设置捕捉候选对象预处理器。
        /// \param snapCandidatesPreprocessor 候选对象预处理器。
        void SetSnapCandidatesPreprocessor(gcmp::OwnerPtr<gcmp::ISnapCandidatesPreprocessor> snapCandidatesPreprocessor);

        /// \brief 获取捕捉候选对象预处理器。
        /// \return 候选对象预处理器指针。
        const gcmp::ISnapCandidatesPreprocessor* GetSnapCandidatesPreprocessor() const;
        gcmp::ISnapCandidatesPreprocessor* GetSnapCandidatesPreprocessor() ;

        // 键盘鼠标组合键
    protected:
        //// \brief 键盘Ctrl键按下判断。
        /// \return  true表示键盘Ctrl键按下，false表示不是键盘Ctrl键按下
        GCMP_OBSOLETE_API(2021/05/01, ActionBase::IsKeyAndButtonPressed(int))
            bool IsCtrlKeyPressed();

        //// \brief 键盘Shift键按下判断。
        /// \return  true表示键盘Shift键按下，false表示不是键盘Shift键按下
        GCMP_OBSOLETE_API(2021/05/01, ActionBase::IsKeyAndButtonPressed(int))
            bool IsShiftKeyPressed();

        //// \brief 键盘Alt键按下判断。
        /// \return  true表示键盘Alt键按下，false表示不是键盘Alt键按下
        GCMP_OBSOLETE_API(2021/05/01, ActionBase::IsKeyAndButtonPressed(int))
            bool IsAltKeyPressed();

        /// \brief 鼠标左键按下判断。
        /// \return  true表示鼠标左键按下，false表示不是鼠标左键按下
        GCMP_OBSOLETE_API(2021/05/01, ActionBase::IsKeyAndButtonPressed(int))
            bool IsLButtonPressed();

        /// \brief 鼠标右键按下判断。
        /// \return  true表示鼠标右键按下，false表示不是鼠标右键按下
        GCMP_OBSOLETE_API(2021/05/01, ActionBase::IsKeyAndButtonPressed(int))
            bool IsRButtonPressed();

        /// \brief 鼠标右键按下判断。
        /// \return  true表示鼠标中键按下，false表示不是鼠标中键按下
        GCMP_OBSOLETE_API(2021/05/01, ActionBase::IsKeyAndButtonPressed(int))
            bool IsMButtonPressed();

        /// \brief 判断指定按键是否按下。
        /// 
        /// \param code 鼠标按键和键盘按键值，取值跟随系统（比如windows系统则使用windows声明的键值，目前只支持windows系统）
        /// \return true表示按键按下，false表示没有按下
        bool IsKeyAndButtonPressed(int code);

        // 临时对象
    protected:
        /// \brief 绘制临时对象GraphicsElementShape。
        void DrawTempGraphicsElementShape(gcmp::OwnerPtr<gcmp::IGraphicsElementShape> upGrep);

        /// \brief GGroup版本的绘制临时对象GraphicsElementShape。
        void DrawTempGraphicsNodeGroup(gcmp::OwnerPtr<gcmp::IGraphicsNodeGroup> upGgroup);

        /// \brief 清理临时element中的grep。
        void CleanupTempGraphicsShape();

        /// \brief 得到画临时图形的PureGraphicsElement的id
        gcmp::ElementId GetTempElementId() const;


        // 供子类调用的功能
    protected:
        /// \brief 用户按Enter和Retrun键是否退出
        /// \return 立即退出返回true，否则返回false
        bool IsReturnKeyNeedCanceled() const;

        /// \brief 设置用户按Enter和Retrun键是否退出
        ///  \param isReturnKeyNeedCanceled 用户按Enter和Retrun键是否退出
        void SetReturnKeyNeedCanceled(bool isReturnKeyNeedCanceled);

        /// \brief 启动一个子Action
        /// \param upAction 当前Action创建的子Action
        void StartChildAction(gcmp::OwnerPtr<gcmp::IAction> upAction);

        /// \brief 撤消子Action（弹出子Action及以上所有Action）
        void CancelChildAction();

        /// \brief 标记此Action的退出状态(比如：成功/取消)，之后系统会将此Action退出并将相关状态传递给它的父Action
        void MarkFinishStatus(ActionFinishStatus status);

        /// \brief 标记此Action的退出原因(比如：Esc/Enter等)
        void MarkFinishReason(ActionFinishReason cancelReason);

        /// \brief 设置此Action退出时输出额外数据
        /// 设置后，通过ActionBase::GetActionOutput 获取
        /// 推荐用法是将ActionOutput放在自己的Action内，根据状态调整输出数据，Action上下文自己解释
        /// 此处用于外部原因结束Action，但是需要告知结束原因和结果的情况
        void SetActionOutputData(OwnerPtr<IActionOutputData> outputData);

        friend class InteractivePicker;
        /// \brief 标记此Action的退出时父Action是否也需要退出(如:拖动夹点)
        void MarkParentActionIsNeedCancel(bool isParentActionNeedCancel = true);

        /// \brief 获取子Action的退出原因(比如：Esc/Enter等)
        ActionFinishReason GetChildFinishReson() const;

        /// \brief 主动结束当前Action（目前仅用于范围选择模式，尽量使用MarkFinishStatus）
        void KillMe();

        /// \brief 刷新视图
        void UpdateView() const;

        /// \brief 获取在工作平面的点,仅用于从屏幕上取得的点
        bool ProjectPositionOnWorkPlane(const IUiView* pUIView, const Vector3d& position, Vector3d& intersectPoint);

        //////////////////////////////////////////////////////////////////////////
        // TODO：这个方法去掉工作量巨大，暂时搁置保留，注意：请勿使用！！请勿使用！！！
        // 获取文档指针
        gcmp::IDocument* GetDoc() const;
        //////////////////////////////////////////////////////////////////////////
    private:
        std::wstring GetCursorPathFromCursorType(CursorType cursorType) const;  //通过 CursorType 获得 CursorPath
    protected:
        OwnerPtr<IAction> m_actionImpl;
        //TODO:待重构 供框架调用的初始化函数
        void InitActionInternal(IUiView* pCurrentView);
        friend class ActionManager;
    };

}

